Skip to content

[ISSUE #65]Add RocketMQ-on-DLeger group mode deploy - #87

Open
haifzhu wants to merge 5 commits into
apache:masterfrom
haifzhu:master
Open

[ISSUE #65]Add RocketMQ-on-DLeger group mode deploy#87
haifzhu wants to merge 5 commits into
apache:masterfrom
haifzhu:master

Conversation

@haifzhu

@haifzhu haifzhu commented Sep 26, 2021

Copy link
Copy Markdown

The Commit is added to add RocketMQ-on-DLeger group mode deploy.

@caigy caigy changed the title Add RocketMQ-on-DLeger group mode deploy [ISSUE #65]Add RocketMQ-on-DLeger group mode deploy Jul 13, 2022
@caigy caigy added the enhancement New feature or request label Jul 13, 2022
@zongtanghu

Copy link
Copy Markdown

I see there are some codes conflicts in this pr, can you resolve this pr in your local enviroments?

@RockteMQ-AI

Copy link
Copy Markdown

⚠️ Merge conflict detected

This PR has conflicts with the base branch and cannot be merged. Please rebase or merge the base branch into your branch and resolve the conflicts:

git fetch origin
git checkout master
git rebase origin/main
# resolve conflicts, then:
git push --force-with-lease

This is a one-time reminder. Feel free to @mention me for a re-review after conflicts are resolved.


Automated notification by github-manager-bot

@RockteMQ-AI RockteMQ-AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

This PR modifies 16 file(s) with 2427 lines of diff. No test changes detected — consider adding test coverage.


Automated review by github-manager-bot

Additional notes (not anchored to a changed line)

  • [INFO] README.md:1 — Large diff (2427 lines). Consider breaking into smaller, focused PRs for easier review. (line outside diff)

@@ -34,14 +34,18 @@ type BrokerSpec struct {
Size int `json:"size"`

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No test changes detected alongside source modifications. Consider adding tests to cover the changes.

@RockteMQ-AI RockteMQ-AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

PR received and logged for review. This PR requires detailed code review by a maintainer.

Diff size: 2427 lines
Author: haifzhu (NONE)


Automated review by RockteMQ-AI

@RockteMQ-AI RockteMQ-AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

Review of PR #87: [ISSUE #65]Add RocketMQ-on-DLeger group mode deploy

Findings: 4 issue(s) identified (2 critical).
CLA: unknown

Please address the inline comments above.


Automated review by github-manager-bot

- brokerImage
- imagePullPolicy
- nameServers
- allowRestart

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding enableDLeger to the required list is a breaking API change for the served v1alpha1 Broker CRD. Existing Broker CRs that do not specify this field will fail validation on updates, and the schema does not declare a default. The field should be optional (removed from required) to preserve backward compatibility.

Comment thread README.md
size: 1
# nameServers is the [ip:port] list of name service
nameServers: ""
# Whether enable rocketmq-on-dleger group deploy

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

README documents enableDLeger: false as optional with an implicit default, but the CRD schema marks the field as required and does not declare a default value. The documented behavior does not match the schema.

nameServers:
description: NameServers defines the name service list e.g. 192.168.1.1:9876;192.168.1.2:9876
type: string
enableDLeger:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The diff only updates the CRD schema and README; no controller, StatefulSet generation, or broker configuration code is modified to consume the new enableDLeger field. As submitted, the field has no effect and the RocketMQ-on-DLeger deployment mode is not implemented.

- brokerImage
- imagePullPolicy
- nameServers
- allowRestart

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No controller tests, reconciliation tests, or RBAC changes are included for the new DLeger mode, affinity, or tolerations support. A feature affecting broker topology and scheduling should have test coverage and any required RBAC updates.

@RockteMQ-AI RockteMQ-AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

The PR introduces DLedger group mode with significant structural changes, but has critical blocking issues: an infinite polling loop in DLegerHostIp that can deadlock the controller, a data race on a shared global variable, and a panic when NameService size is 0. Additionally, marking enableDLeger as required in the CRD is a backward-incompatible breaking change.

Findings

  • [CRITICAL] pkg/controller/broker/broker_controller.go:131DLegerHostIp contains an unbounded for {} loop with time.Sleep that polls forever until the Service exists. This blocks the reconcile goroutine indefinitely if the Service creation failed (e.g., RBAC denial, quota exceeded). The controller will deadlock — no other reconcile events can be processed on this worker. Replace with a bounded retry or, better, return the error from Reconcile and let the controller framework requeue with backoff.
  • [WARNING] pkg/controller/broker/broker_controller.go:108CreateService logs the error from r.client.Create but does not return it. The caller proceeds to build a StatefulSet and call DLegerHostIp which will then block forever waiting for the Service that failed to create. This should return an error to the caller so Reconcile can requeue.
  • [WARNING] pkg/controller/broker/broker_controller.go:196 — DLeger peer construction and CreateService/DLegerHostIp calls are unconditional — they run even when broker.Spec.EnableDLeger is false. This creates unnecessary Services and blocks on them for every non-DLedger deployment. Wrap the DLeger-specific logic in an if broker.Spec.EnableDLeger guard.
  • [CRITICAL] pkg/controller/nameservice/nameservice_controller.go:161getNameServiceName panics when nameService.Spec.Size is 0: the loop body never executes, nameserviceName remains "", and nameserviceName[:len(nameserviceName)-1] evaluates to ""[:-1] which is an out-of-bounds slice. Add a guard for Size == 0.
  • [WARNING] pkg/share/share.go:28NameServersServiceStr is a package-level mutable global written by the NameService controller (nameservice_controller.go:168) and read by the Broker controller (broker_controller.go:575). This is a data race in a concurrent controller-runtime environment where both reconcilers run in parallel goroutines. Use a thread-safe mechanism (mutex, or pass via CR status) instead of a bare global string.
  • [WARNING] deploy/crds/rocketmq_v1alpha1_broker_crd.yaml:843enableDLeger is added to the required fields list. This is a breaking change — any existing Broker CR that does not specify enableDLeger will be rejected by the API server on update. New optional fields should not be required; rely on the Go zero-value (false) or add a default via admission webhook.
  • [WARNING] pkg/apis/rocketmq/v1alpha1/broker_types.go:46Affinity is declared as corev1.Affinity (value type) without omitempty. An empty Affinity struct will always be serialized into the StatefulSet pod spec even when the user sets none, and the CRD makes it required. Use *corev1.Affinity (pointer) with json:"affinity,omitempty" so it is nil when unset.
  • [WARNING] pkg/apis/rocketmq/v1alpha1/broker_types.go:47Tolerations and other new fields (Env in NameService) also lack omitempty in their JSON tags. This forces users to provide empty arrays in their CRs and makes the CRD schema unnecessarily strict.
  • [WARNING] pkg/controller/console/console_controller.go:198 — The hardcoded JAVA_OPTS env var (containing -Drocketmq.namesrv.addr=...) was removed without a replacement mechanism. Users who relied on the operator injecting the name server address into the console will now get a console that cannot connect to RocketMQ. The example CRs add JAVA_OPTS manually, but existing deployments that upgrade will silently break. Consider keeping a default or documenting this as a breaking change.
  • [INFO] pkg/controller/broker/broker_controller.go:137DLegerHostIp returns svcObj.Spec.ClusterIP from a Service that may have just been created. For non-headless Services (the broker services here have no ClusterIP: "None"), the ClusterIP is assigned asynchronously by the API server. A tight poll loop may read an empty ClusterIP. Consider using a headless Service + pod DNS (e.g., <pod>.<svc>.<ns>.svc.cluster.local) for stable addressing.
  • [INFO] pkg/controller/nameservice/nameservice_controller.go:344 — The comment says // Set bind for broker crd and svc but this is in the NameService controller and the owner reference is set to nameService, not a broker. Copy-paste artifact — update the comment to reflect the actual owner.
  • [INFO] images/broker/alpine/brokerGenConfig.sh:34 — The shell test [ ! -z $ENABLE_DLEGER ] is unquoted. If ENABLE_DLEGER is unset (not just empty), this becomes [ ! -z ] which evaluates to true (tests the -z flag itself as a non-empty string), enabling DLedger unintentionally. Quote it: [ ! -z "$ENABLE_DLEGER" ] or use [ -n "$ENABLE_DLEGER" ].
  • [WARNING] pkg/controller/broker/broker_controller.go:241 — In the scale-down/update path, CreateService is called for replicas inside the nameServers-update loop but never for the master (replicaIndex 0). If the master's Service was deleted externally, it will not be recreated during updates, while replica services will be. This is inconsistent with the initial creation path at line 196.
  • [INFO] pkg/controller/broker/broker_controller.go:571share.NameServersStr was replaced with share.NameServersServiceStr in the broker env. NameServersServiceStr is built from the CR name and assumes a specific headless service naming convention. If the NameService controller hasn't reconciled yet, this value may be stale or empty, causing brokers to start with no valid name server address.
  • [INFO] pkg/share/share.go:28 — The comment on NameServersServiceStr says // NameServersStr is the name server list — it's a copy of the comment above it. Update to describe what this variable actually holds (service-based name server addresses).

Automated review by github-manager-bot

reqLogger.Info("Get DLeger Host IP lists...")

statefulSetName := getBrokerStatefulSetName(broker, brokerGroupIndex, replicaIndex)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DLegerHostIp contains an unbounded for {} loop with time.Sleep that polls forever until the Service exists. This blocks the reconcile goroutine indefinitely if the Service creation failed (e.g., RBAC denial, quota exceeded). The controller will deadlock — no other reconcile events can be processed on this worker. Replace with a bounded retry or, better, return the error from Reconcile and let the controller framework requeue with backoff.

scheme *runtime.Scheme
}

func (r *ReconcileBroker) CreateService(request reconcile.Request, broker *rocketmqv1alpha1.Broker, brokerGroupIndex int, replicaIndex int) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CreateService logs the error from r.client.Create but does not return it. The caller proceeds to build a StatefulSet and call DLegerHostIp which will then block forever waiting for the Service that failed to create. This should return an error to the caller so Reconcile can requeue.

replicaPerGroup := broker.Spec.ReplicaPerGroup
reqLogger.Info("brokerGroupNum=" + strconv.Itoa(share.GroupNum) + ", replicaPerGroup=" + strconv.Itoa(replicaPerGroup))

for brokerGroupIndex := 0; brokerGroupIndex < share.GroupNum; brokerGroupIndex++ {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DLeger peer construction and CreateService/DLegerHostIp calls are unconditional — they run even when broker.Spec.EnableDLeger is false. This creates unnecessary Services and blocks on them for every non-DLedger deployment. Wrap the DLeger-specific logic in an if broker.Spec.EnableDLeger guard.

return r.updateNameServiceStatus(instance, request, true)
}

func getNameServiceName(nameService *rocketmqv1alpha1.NameService) string {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getNameServiceName panics when nameService.Spec.Size is 0: the loop body never executes, nameserviceName remains "", and nameserviceName[:len(nameserviceName)-1] evaluates to ""[:-1] which is an out-of-bounds slice. Add a guard for Size == 0.

Comment thread pkg/share/share.go
// NameServersStr is the name server list
NameServersStr = ""

// NameServersStr is the name server list

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NameServersServiceStr is a package-level mutable global written by the NameService controller (nameservice_controller.go:168) and read by the Broker controller (broker_controller.go:575). This is a data race in a concurrent controller-runtime environment where both reconcilers run in parallel goroutines. Use a thread-safe mechanism (mutex, or pass via CR status) instead of a bare global string.

func (r *ReconcileNameService) CreateService(request reconcile.Request, nameService *rocketmqv1alpha1.NameService) {
reqLogger := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name)
reqLogger.Info("Create a Name Service...")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment says // Set bind for broker crd and svc but this is in the NameService controller and the owner reference is set to nameService, not a broker. Copy-paste artifact — update the comment to reflect the actual owner.

sed -i 's/brokerRole=.*/brokerRole=SLAVE/g' $BROKER_CONFIG_FILE
fi

# Enable RocketMQ-on-DLedger Group

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The shell test [ ! -z $ENABLE_DLEGER ] is unquoted. If ENABLE_DLEGER is unset (not just empty), this becomes [ ! -z ] which evaluates to true (tests the -z flag itself as a non-empty string), enabling DLedger unintentionally. Quote it: [ ! -z "$ENABLE_DLEGER" ] or use [ -n "$ENABLE_DLEGER" ].

@@ -193,8 +241,14 @@ func (r *ReconcileBroker) Reconcile(request reconcile.Request) (reconcile.Result
for brokerGroupIndex := 0; brokerGroupIndex < broker.Spec.Size; brokerGroupIndex++ {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the scale-down/update path, CreateService is called for replicas inside the nameServers-update loop but never for the master (replicaIndex 0). If the master's Service was deleted externally, it will not be recreated during updates, while replica services will be. This is inconsistent with the initial creation path at line 196.

@@ -453,10 +569,10 @@ func (r *ReconcileBroker) getBrokerStatefulSet(broker *rocketmqv1alpha1.Broker,

}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

share.NameServersStr was replaced with share.NameServersServiceStr in the broker env. NameServersServiceStr is built from the CR name and assumes a specific headless service naming convention. If the NameService controller hasn't reconciled yet, this value may be stale or empty, causing brokers to start with no valid name server address.

Comment thread pkg/share/share.go
// NameServersStr is the name server list
NameServersStr = ""

// NameServersStr is the name server list

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment on NameServersServiceStr says // NameServersStr is the name server list — it's a copy of the comment above it. Update to describe what this variable actually holds (service-based name server addresses).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants